#include "StdAfx.h"

CommandHandler* Singleton<CommandHandler>::m_instance = 0;

template<typename CHAR_TYPE>
CHAR_TYPE* stristr(CHAR_TYPE*  szStringToBeSearched, const CHAR_TYPE* szSubstringToSearchFor)
{
	CHAR_TYPE* pPos = NULL;
	CHAR_TYPE* szCopy1 = NULL;
	CHAR_TYPE* szCopy2 = NULL;

	if(!szStringToBeSearched || !szSubstringToSearchFor)
	  return szStringToBeSearched;

	if(!_tcslen(szSubstringToSearchFor))
	  return szStringToBeSearched;

	szCopy1 = _tcslwr(_tcsdup(szStringToBeSearched));
	szCopy2 = _tcslwr(_tcsdup(szSubstringToSearchFor));

	if(!szCopy1 || !szCopy2)
	{
	  free((void*)szCopy1);
	  free((void*)szCopy2);
	  return NULL;
	}

	pPos = strstr(szCopy1, szCopy2);

	if(pPos)
	  pPos = szStringToBeSearched + (pPos - szCopy1);

	free((void*)szCopy1);
	free((void*)szCopy2);

	return pPos;
}

CommandHandler::CommandHandler()
{
	m_mainCmds = new ChatCommandTable;
	ChatCommandTable* lookupCmds = new ChatCommandTable;
	ChatCommandTable* chunkCmds = new ChatCommandTable;
	ChatCommandTable* dbcAreaCmds = new ChatCommandTable;
	ChatCommandTable* dbcAreaChangeCmds = new ChatCommandTable;
	m_mainCmds->InsertCmd(0, "lookup", "", lookupCmds);
	m_mainCmds->InsertCmd(0, "chunk", "", chunkCmds);
	m_mainCmds->InsertCmd(0, "dbcArea", "", dbcAreaCmds);
	lookupCmds->InsertCmd(&CommandHandler::HandleLookupAreaId, "areaid", "Searches the areaid of the given name.");
	chunkCmds->InsertCmd(&CommandHandler::HandleSetAreaId, "areaid", "Sets the areaid to the specified id.");
	dbcAreaCmds->InsertCmd(&CommandHandler::HandleCreateAreaId, "add", "Creates an area with the specified id.");
	dbcAreaCmds->InsertCmd(0, "change", "", dbcAreaChangeCmds);
	dbcAreaChangeCmds->InsertCmd(&CommandHandler::HandleModifyAreaDbcFlags, "flags", "Changes the areaflags of the id");
	dbcAreaChangeCmds->InsertCmd(&CommandHandler::HandleModifyAreaDbcZone, "map", "Changes the mapid of the areaid.");
	dbcAreaChangeCmds->InsertCmd(&CommandHandler::HandleModifyAreaDbcParent, "parent", "Changes the parentid of the areaid.");
}

void CommandHandler::HandleInput(std::string szInput)
{
	std::string::size_type pos = szInput.find(' ');
	if(pos == std::string::npos)
	{
		ChatCommand* pCmd = 0;
		if((pCmd = m_mainCmds->GetCommand(szInput)))
		{
			if(pCmd->ChatProc)
				(this->*(pCmd->ChatProc))("");
			else if(pCmd->subcommands)
				pCmd->subcommands->PrintSubcmds();
		}
	}
	else
	{
		std::string subcmd = szInput.substr(0, pos);
		ChatCommand* pCmd = 0;
		if((pCmd = m_mainCmds->GetCommand(subcmd)))
		{
			if(pCmd->ChatProc)
				(this->*(pCmd->ChatProc))(szInput.substr(pos + 1, szInput.length()));
			else if(pCmd->subcommands)
				HandleCommand(pCmd, szInput.substr(pos + 1, szInput.length()));
		}
	}
}

void CommandHandler::HandleCommand(ChatCommand* pCmd2, std::string args)
{
	std::string::size_type pos = args.find(' ');
	if(pos == std::string::npos)
	{
		ChatCommand* pCmd = 0;
		if((pCmd = pCmd2->subcommands->GetCommand(args)))
		{
			if(pCmd->ChatProc)
				(this->*(pCmd->ChatProc))("");
			else if(pCmd->subcommands)
				pCmd->subcommands->PrintSubcmds();
		}
	}
	else
	{
		std::string subcmd = args.substr(0, pos);
		ChatCommand* pCmd = 0;
		if((pCmd = pCmd2->subcommands->GetCommand(subcmd)))
		{
			if(pCmd->ChatProc)
				(this->*(pCmd->ChatProc))(args.substr(pos + 1, args.length()));
			else if(pCmd->subcommands)
				HandleCommand(pCmd, args.substr(pos + 1, args.length()));
		}
	}
}

void ChatCommandTable::InsertCmd(void (CommandHandler::*cp)(std::string), std::string name, std::string helptext, ChatCommandTable* subcmds)
{
	ChatCommand* commi = new ChatCommand();
	commi->ChatProc = cp;
	commi->InternalName = name;
	commi->subcommands = subcmds;
	commi->HelpText = helptext;
	m_commands.insert(commi);
}

void ChatCommandTable::PrintSubcmds()
{
	sChat.AddText("Available subcommands:", 10);
	std::set<ChatCommand*>::iterator itr = m_commands.begin();
	std::set<ChatCommand*>::iterator end = m_commands.end();

	for( ; itr != end; ++itr)
	{
		sChat.AddText((*itr)->InternalName);
	}
}

ChatCommand* ChatCommandTable::GetCommand(std::string cmdtext)
{
	if(!m_commands.size())
		return 0;
	std::set<ChatCommand*>::iterator itr = m_commands.begin();
	std::set<ChatCommand*>::iterator end = m_commands.end();

	for( ; itr != end; ++itr)
	{
		if((*itr)->InternalName == cmdtext)
			return (*itr);
	}

	return 0;
}

void ChatCommand::PrintHelpText()
{
	sChat.AddText(HelpText);
}

void CommandHandler::HandleLookupAreaId(std::string args)
{
	if(args == "")
	{
		sChat.AddText("Useage: lookup areaid <substring of the area>", 11);
		return;
	}
	if(args.length() <= 3)
	{
		sChat.AddText("Substring should have more then 3 chars!", 12);
		return;
	}
	ui32 areaRow = dbcArea.GetRowCount();
	sChat.AddText("Searching areaids...", 13);
	for(ui32 i = 0, found = 0; i < areaRow; ++i)
	{
		AreaTableEntry* e = dbcArea.GetRow(i);
		if(e->name && stristr(e->name, args.c_str()))
		{
			char msg[255];
			sprintf_s<255>(msg, "%u - %s", e->id, e->name);
			sChat.AddText(msg);
			++found;
		}
		if(found >= 50)
		{
			sChat.AddText("Found more then 50 results, aborting!", 14);
			return;
		}
	}
	sChat.AddText("Finished...", 15);
}

void CommandHandler::HandleSetAreaId(std::string szInput)
{
	if(szInput == "")
	{
		sChat.AddText("Useage: chunk areaid <new area id>", 16);
		return;
	}
	ui32 areaId = 0;
	if(sscanf(szInput.c_str(), "%u", &areaId) != 1)
	{
		sChat.AddText("Useage: chunk areaid <new area id>", 16);
		return;
	}
	AreaTableEntry* e = dbcArea.LookupEntry(areaId);
	if(!e)
	{
		sChat.AddText("This areaid doesnt exist!", 8);
		return;
	}
	Vertex3F cp = sCamera.GetCamPosition();
	ui32 indexX = (ui32)floor(cp.z / TILESIZE);
	ui32 indexY = (ui32)floor(cp.x / TILESIZE);
	ADTModel* model = sD3D.GetADT(indexX, indexY);
	if(model)
	{
		float rX = cp.z - (indexX * TILESIZE);
		float rY = cp.x - (indexY * TILESIZE);
		ui32 chunkX = (ui32)floor(rX / CHUNKSIZE);
		ui32 chunkY = (ui32)floor(rY / CHUNKSIZE);
		ADTChunk* cnk = model->GetChunk(chunkX, chunkY);
		if(cnk)
		{
			cnk->SetAreaId(areaId);
			sChat.AddText("Areaid changed!", 17);
		}
	}
}

void CommandHandler::HandleCreateAreaId(std::string szInput)
{
	ui32 areaId;
	std::string::size_type p = szInput.find(' ');
	if(p == std::string::npos)
		return;
	std::string area = szInput.substr(0, p);
	if(sscanf(area.c_str(), "%u", &areaId) != 1)
	{
		sChat.AddText("Usage: dbcArea add <areaid> <areaname>", 18);
		return;
	}
	AreaTableEntry* e = dbcArea.LookupEntry(areaId);
	if(e)
	{
		sChat.AddText("This areaid already exists.", 19);
		return;
	}
	szInput = szInput.substr(p + 1, szInput.length());
	ui32 stringPos = dbcArea.AddString(szInput);
	AreaTableEntry* pEntry = new AreaTableEntry;
	pEntry->areaLevel = 0;
	pEntry->explore = 0;
	pEntry->flags = 0;
	pEntry->id = areaId;
	pEntry->map_id = 1;
	pEntry->name = (char*)stringPos;
	memset(pEntry->nneed, 0, 8);
	pEntry->rec_id = 0;
	pEntry->soundRef = 0;
	pEntry->soundsRef = 0;
	pEntry->unk = 0;
	pEntry->zoneRef = 0;
	pEntry->zoneSec = 0;
	dbcArea.AddRecord(pEntry);
	dbcArea.SaveToFile();
	pEntry = dbcArea.LookupEntry(areaId);
	pEntry->name = new char[szInput.length()];
	strcpy(pEntry->name, szInput.c_str());
	sChat.AddText("Areaid added!", 9);
}

void CommandHandler::HandleModifyAreaDbcFlags(std::string szInput)
{
	if(szInput == "")
	{
		sChat.AddText("Usage: dbcArea change flags <areaid> <new flags>", 20);
		return;
	}
	ui32 areaId = 0, flags = 0;
	if(sscanf(szInput.c_str(), "%u %u", &areaId, &flags) != 2)
	{
		sChat.AddText("Usage: dbcArea change flags <areaid> <new flags>", 20);
		return;
	}
	AreaTableEntry* pEntry;
	dbcArea.GetEntry(areaId, &pEntry);
	if(!pEntry)
	{
		sChat.AddText("This areaid does not exist!", 8);
		return;
	}

	dbcArea.SetData(areaId, 4, flags);
	dbcArea.SaveToFile();
	sChat.AddText("Flags changed!", 21);
}

void CommandHandler::HandleModifyAreaDbcZone(std::string szInput)
{
	if(szInput == "")
	{
		sChat.AddText("Usage: dbcArea change map <areaid> <new map>", 22);
		return;
	}
	ui32 areaId = 0, flags = 0;
	if(sscanf(szInput.c_str(), "%u %u", &areaId, &flags) != 2)
	{
		sChat.AddText("Usage: dbcArea change map <areaid> <new map>", 22);
		return;
	}
	AreaTableEntry* pEntry;
	dbcArea.GetEntry(areaId, &pEntry);
	if(!pEntry)
	{
		sChat.AddText("This areaid does not exist!", 8);
		return;
	}
	dbcArea.SetData(areaId, 1, flags);
	dbcArea.SaveToFile();
	sChat.AddText("Map changed!", 23);
}

void CommandHandler::HandleModifyAreaDbcParent(std::string szInput)
{
	if(szInput == "")
	{
		sChat.AddText("Usage: dbcArea change parent <areaid> <parentid>", 24);
		return;
	}
	ui32 areaId = 0, flags = 0;
	if(sscanf(szInput.c_str(), "%u %u", &areaId, &flags) != 2)
	{
		sChat.AddText("Usage: dbcArea change parent <areaid> <parentid>", 24);
		return;
	}
	AreaTableEntry* pEntry;
	dbcArea.GetEntry(areaId, &pEntry);
	if(!pEntry)
	{
		sChat.AddText("This areaid does not exist!", 8);
		return;
	}
	pEntry = dbcArea.LookupEntry(flags);
	if(!pEntry)
	{
		sChat.AddText("This parent does not exist!", 25);
		return;
	}
	dbcArea.SetData(areaId, 2, flags);
	dbcArea.SaveToFile();
	sChat.AddText("Parent changed!", 26);
}